package com.jh.paymentchannel.business.impl;

import cn.jh.common.exception.ServiceException;
import cn.jh.common.proxy.CongMingPayProxyService;
import cn.jh.common.utils.CommonConstants;
import cn.jh.common.utils.DateUtil;
import com.jh.paymentchannel.business.CongMingPayBusiness;
import com.jh.paymentchannel.pojo.CmOrderQuery;
import com.jh.paymentchannel.pojo.CongMingOrder;
import com.jh.paymentchannel.pojo.CongMingOrderQuery;
import com.jh.paymentchannel.pojo.MicroPay;
import com.jh.paymentchannel.pojo.enums.CmMicroPayStatus;
import com.jh.paymentchannel.pojo.enums.CmPayType;
import com.jh.paymentchannel.pojo.enums.CmQueryPayStatus;
import com.jh.paymentchannel.pojo.enums.CmRefundStatus;
import com.jh.paymentchannel.repository.CongMingOrderQueryRepository;
import com.jh.paymentchannel.repository.CongMingOrderRepository;
import com.jh.paymentchannel.util.Util;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.locks.Lock;

@Service
public class CongMingPayBusinessImpl implements CongMingPayBusiness {

    private static final Logger log = LoggerFactory.getLogger(CongMingPayBusinessImpl.class);

    @Value("${congMingPay.key}")
    private String key;

    @Value("${congMingPay.debug}")
    private String debug;

    @Value("${congMingPay.prePayRedirectUrl}")
    private String prePayRedirectUrl;

    @Value("${congMingPay.buyPayRedirectUrl}")
    private String buyPayRedirectUrl;

    private static final String debug_shopId = "49ee540573d6227922c488def7e09aff";

    @Autowired
    private Util util;
    @Autowired
    private CongMingOrderRepository congMingOrderRepository;
    @Autowired
    private CongMingOrderQueryRepository congMingOrderQueryRepository;

    /**
     * 格式化sign参数
     * @param map
     * @return
     */
    private static String urlEncode(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry i : map.entrySet()) {
            sb.append(i.getKey()).append("=").append(i.getValue()).append("&");
        }
        return sb.substring(0, sb.length() - 1);
    }

    /**
     * 生成订单号：orderId
     * @return
     */
    private static String createOrderId() {
        synchronized (Lock.class) {
            Random rand = new Random();
            int randNum = rand.nextInt(99999) + 100000;
            return DateUtil.getyyyyMMddHHmmssSSSDateFormat(Calendar.getInstance().getTime()) + randNum;
        }
    }

    /**
     * 根据商户id获取该商户的费率信息
     * @param shopId
     * @return
     */
    private List<Map<String, String>> getCongMingRate(String shopId) throws ServiceException {
        List<Map<String, String>> list = new ArrayList<>();
        try {
            URI uri = util.getServiceUrl("user", "error url request!");
            String url = uri + "/v1.0/user/congmingrate/getcongmingratelistbyshopid";

            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("shopId", shopId);

            RestTemplate restTemplate = new RestTemplate();
            String result = restTemplate.postForObject(url, body, String.class);
            JSONObject jsonObject = JSONObject.fromObject(result);
            String respCode = jsonObject.getString(CommonConstants.RESP_CODE);
            String respMsg = jsonObject.getString(CommonConstants.RESP_MESSAGE);
            if (!CommonConstants.SUCCESS.equals(respCode)) {
                throw new ServiceException(respMsg);
            }

            // 获取成功
            JSONArray jsonArray = jsonObject.getJSONArray(CommonConstants.RESULT);
            for (int i = 0; i < jsonArray.size(); i++) {
                jsonObject = JSONObject.fromObject(jsonArray.get(i));
                String channelTag = jsonObject.getString("channelTag");
                String rate = jsonObject.getString("rate");
                String userId = jsonObject.getString("userId");

                Map<String, String> map = new HashMap<>();
                map.put("channelTag", channelTag);
                map.put("rate", rate);
                map.put("userId", userId);
                list.add(map);
            }
        } catch (RuntimeException e) {
            throw new ServiceException(e.getMessage());
        }
        return list;
    }

    /**
     * 根据shopId获取商户注册信息
     * @param shopId
     * @return
     */
    private Map<String, String> getShopInfo(String shopId) throws ServiceException {
        try {
            URI uri = util.getServiceUrl("user", "error url request!");
            String url = uri + "/v1.0/user/shopregister/getshopregisterbyshopid";

            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("shopId", shopId);

            RestTemplate restTemplate = new RestTemplate();
            String result = restTemplate.postForObject(url, body, String.class);

            JSONObject jsonObject = JSONObject.fromObject(result);
            String respCode = jsonObject.getString(CommonConstants.RESP_CODE);
            String respMsg = jsonObject.getString(CommonConstants.RESP_MESSAGE);
            if (!CommonConstants.SUCCESS.equals(respCode)) {
                throw new ServiceException(respMsg);
            }
            result = jsonObject.getString(CommonConstants.RESULT);
            jsonObject = JSONObject.fromObject(result);
            String shopName = jsonObject.getString("shopName");
            String userId = jsonObject.getString("userId");
            Map<String, String> res = new HashMap<>();
            res.put("shopName", shopName);
            res.put("userId", userId);
            return res;
        } catch (RuntimeException r) {
            throw new ServiceException(r.getMessage());
        }
    }

    @Override
    @Transactional
    public List<Map<String, Object>> prePay(String shopId, BigDecimal money, String payType) throws ServiceException {
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> resMap1 = new HashMap<>();
        Map<String, Object> resMap2 = new HashMap<>();
        try {
            // 根据shopId获取商户信息
            Map<String, String> si = getShopInfo(shopId);
            String shopName = si.get("shopName");
            String userId = si.get("userId");

            // 1、生成订单号
            String orderId = createOrderId();

            // 2、获取当前商户的通道费率信息，并计算金额
//            BigDecimal weixinMoney = BigDecimal.ZERO;
//            BigDecimal alipayMoney = BigDecimal.ZERO;
//            List<Map<String, String>> mingRateList = getCongMingRate(shopId);
//            for (Map<String, String> mingRateMap : mingRateList) {
//                if (null != mingRateMap) {
//                    String channelTag = mingRateMap.get("channelTag");
//                    String rate = mingRateMap.get("rate");
//
//                    if (CommonConstants.WEIXIN_PAY.equals(channelTag)) {
//                        weixinMoney = money.subtract(money.multiply(new BigDecimal(rate)).setScale(2, BigDecimal.ROUND_UP)); // 四舍五入
//                    }
//                    if (CommonConstants.ALI_PAY.equals(channelTag)) {
//                        alipayMoney = money.subtract(money.multiply(new BigDecimal(rate)).setScale(2, BigDecimal.ROUND_UP));
//                    }
//                }
//            }

            // TODO 3、根据开店管家shopId获取快银支付shopId--待完成
            String congMingShopId = "";

            // 4、格式化sign参数
            Map<String, Object> map = new HashMap<>();
            map.put("money", money);
            if ("true".equals(debug)) {
                map.put("shopId", debug_shopId);
            } else {
                map.put("shopId", congMingShopId);
            }
            map.put("orderId", orderId);
            map.put("key", key);
            String str = urlEncode(map);

            // 5、生成sign
            String sign = DigestUtils.md5Hex(str).toUpperCase();
            if ("true".equals(debug)) {
                String weixin = CongMingPayProxyService.prePay(debug_shopId, orderId, String.valueOf(money), "weixin", prePayRedirectUrl, sign);
                String alipay = CongMingPayProxyService.prePay(debug_shopId, orderId, String.valueOf(money), "alipay", prePayRedirectUrl, sign);
                addList(list, resMap1, resMap2, weixin, alipay);
//                return list;
            } else {
                // 6、进行预支付
                String weixin = CongMingPayProxyService.prePay(congMingShopId, orderId, String.valueOf(money), "weixin", prePayRedirectUrl, sign);
                String alipay = CongMingPayProxyService.prePay(congMingShopId, orderId, String.valueOf(money), "alipay", prePayRedirectUrl, sign);
                addList(list, resMap1, resMap2, weixin, alipay);
            }


            // 7、新增订单信息
            CongMingOrder congMingOrder = new CongMingOrder();
            congMingOrder.setUserId(Long.parseLong(userId));
            congMingOrder.setShopId(shopId);
            congMingOrder.setOrderId(orderId);
            congMingOrder.setSign(sign);
            congMingOrder.setCreateTime(Calendar.getInstance().getTime());
            congMingOrderRepository.save(congMingOrder);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
        return list;
    }

    private void addList(List<Map<String, Object>> list, Map<String, Object> resMap1, Map<String, Object> resMap2, String weixin, String alipay) {
        resMap1.put("type", "weixin");
        resMap1.put("url", weixin);
        list.add(resMap1);

        resMap2.put("type", "alipay");
        resMap2.put("url", alipay);
        list.add(resMap2);
    }

    @Override
    @Transactional
    public Map<String, Object> toMicroPay(String shopId, BigDecimal money, String authCode) throws ServiceException {
        Map<String, Object> res = new HashMap<>();
        JSONObject jsonObject;
        String result;
        try {
            // 根据shopId获取商户信息
            Map<String, String> si = getShopInfo(shopId);
            String shopName = si.get("shopName");
            String userId = si.get("userId");

            // 1、生成订单id
            String orderId = createOrderId();

            // TODO 2、根据开店管家shopId获取快银支付shopId--待完成
            String congMingShopId = "";

            // 3、格式化sign参数
            Map<String, Object> map = new HashMap<>();
            if ("true".equals(debug)) {
                map.put("shopId", debug_shopId);
            } else {
                map.put("shopId", congMingShopId);
            }
            map.put("money", money);
            map.put("authCode", authCode);
            map.put("orderId", orderId);
            map.put("key", key);
            String str = urlEncode(map);
            // 4、生成sign
            String sign = DigestUtils.md5Hex(str).toUpperCase();

            if ("true".equals(debug)) {
                result = CongMingPayProxyService.microPay(debug_shopId, orderId, String.valueOf(money), authCode, sign);
                log.info("debug--条码支付结果：{}", result);

                jsonObject = JSONObject.fromObject(result);
                String pay_status = jsonObject.getString("pay_status");
                if ("FAIL".equals(pay_status)) {
                    String pay_msg = jsonObject.getString("pay_msg");
                    throw new ServiceException(pay_msg);
                }

                MicroPay microPay = (MicroPay) JSONObject.toBean(jsonObject, MicroPay.class);
                if (null != microPay) {
                    CongMingOrder congMingOrder = new CongMingOrder();
                    congMingOrder.addCongMingOrder(microPay);
                    congMingOrder.setUserId(Long.parseLong(userId));
                    congMingOrder.setShopId(shopId);
                    congMingOrderRepository.save(congMingOrder);

                    String payStatus = congMingOrder.getPayStatus();
                    String orderType = congMingOrder.getOrderType();
                    BigDecimal payable = congMingOrder.getPayable();
                    BigDecimal paidOut = congMingOrder.getPaidOut();

                    res.put("orderId", orderId);
                    res.put("payStatus", CmMicroPayStatus.of(payStatus).getDesc());
                    res.put("orderType", CmPayType.of(orderType).getDesc());
                    res.put("payable", payable);
                    res.put("paidOut", paidOut);
                    return res;
                }
            }

            // 5、进行条码支付
            result = CongMingPayProxyService.microPay(congMingShopId, orderId, String.valueOf(money), authCode, sign);
            // TODO 6、处理返回的JSON数据
            log.info("条码支付返回成功：{}", result);

            jsonObject = JSONObject.fromObject(result);
            String pay_status = jsonObject.getString("pay_status");
            if ("FAIL".equals(pay_status)) {
                String pay_msg = jsonObject.getString("pay_msg");
                throw new ServiceException(pay_msg);
            }

            MicroPay microPay = (MicroPay) JSONObject.toBean(jsonObject, MicroPay.class);
            if (null != microPay) {
                CongMingOrder congMingOrder = new CongMingOrder();
                congMingOrder.addCongMingOrder(microPay);
                congMingOrder.setUserId(Long.parseLong(userId));
                congMingOrder.setShopId(shopId);
                congMingOrderRepository.save(congMingOrder);

                String payStatus = congMingOrder.getPayStatus();
                String orderType = congMingOrder.getOrderType();
                BigDecimal payable = congMingOrder.getPayable();
                BigDecimal paidOut = congMingOrder.getPaidOut();

                res.put("orderId", orderId);
                res.put("payStatus", CmMicroPayStatus.of(payStatus).getDesc());
                res.put("orderType", CmPayType.of(orderType).getDesc());
                res.put("payable", payable);
                res.put("paidOut", paidOut);
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
        return res;
    }

    @Override
    public Map<String, Object> toBuyPay(String shopId, BigDecimal money) throws ServiceException {
        // 1、生成订单id
        String orderId = createOrderId();
        // 2、格式化sign参数
        Map<String, Object> map = new HashMap<>();
        return null;
    }

    @Override
    @Transactional
    public void prePayRedirectUrl(String resultCode, String resultMsg, String congMingShopId, BigDecimal money, String type, String timeEnd, String orderId, String outTradeNo, String sign) {
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("money", money);
            map.put("orderId", orderId);
            map.put("result_code", resultCode);
            map.put("shopId", congMingShopId);
            map.put("key", key);

            String str = urlEncode(map);
            String s = DigestUtils.md5Hex(str);

            CongMingOrder existOrder = congMingOrderRepository.getByOrder(orderId);
            if(null == existOrder) {
                return;
            }

            if (s.equals(sign)) {
                // TODO 更新订单信息
                if (CommonConstants.BASE_SUCCESS.equals(resultCode)) {
                    // 支付成功
                    existOrder.setCongMingShopId(congMingShopId);
                    existOrder.setMoney(money);
                    existOrder.setTimeEnd(timeEnd);
                } else {
                    // 支付失败
                    existOrder.setResultMsg(resultMsg);
                    existOrder.setMoney(money);
                }
                if (StringUtils.isNotEmpty(type)) {
                    existOrder.setType(type);
                }
                existOrder.setOutTradeNo(outTradeNo);
                congMingOrderRepository.save(existOrder);
            }
        } catch (Exception e) {
            log.error("预支付成功后更新支付流水信息异常，orderId = {}", orderId);
            e.printStackTrace();
        }
    }

    @Override
    @Transactional
    public Map<String, Object> queryPay(String shopId, String orderId) throws ServiceException {
        try {
            Map<String, Object> res = new HashMap<>();
            // TODO 1、根据开店管家shopId获取快银支付shopId--待完成
            String congMingShopId = "";
            if ("true".equals(debug)) {
                String result = CongMingPayProxyService.query(debug_shopId, orderId);
                JSONObject jsonObject = JSONObject.fromObject(result);
                String resultCode = jsonObject.getString("result_code");
                if ("FAIL".equals(resultCode)) {
                    String resultMsg = jsonObject.getString("result_msg");
                    throw new ServiceException(resultMsg);
                }
                CmOrderQuery cmOrderQuery = (CmOrderQuery) JSONObject.toBean(jsonObject, CmOrderQuery.class);
                // 查询是否已存在该记录，若存在则更新记录，否则新增记录
                CongMingOrderQuery existQuery = congMingOrderQueryRepository.queryByOrderId(orderId);
                if (null == existQuery) {
                    existQuery = new CongMingOrderQuery();
                    existQuery.addCongMingOrderQuery(cmOrderQuery, shopId);
                    existQuery.setCreateTime(Calendar.getInstance().getTime());
                    congMingOrderQueryRepository.save(existQuery);
                } else {
                    existQuery.addCongMingOrderQuery(cmOrderQuery, shopId);
                    congMingOrderQueryRepository.save(existQuery);
                }

                BigDecimal payable = existQuery.getPayable();
                long status = existQuery.getStatus();
                resultCode = existQuery.getResultCode();
                String cmOrderId = existQuery.getCmOrderId();
                Date time = existQuery.getTime();
                String timeEnd = existQuery.getTimeEnd();
                long refundStatus = existQuery.getRefundStatus();
                String cmShopId = existQuery.getCmShopId();
                BigDecimal money = existQuery.getMoney();

                res.put("payable", payable);
                res.put("status", CmQueryPayStatus.of(status).getDesc());
                res.put("resultCode", resultCode);
                res.put("cmOrderId", cmOrderId);
                res.put("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));
                res.put("timeEnd", timeEnd);
                res.put("refundStatus", CmRefundStatus.of(refundStatus).getDesc());
                res.put("cmShopId", cmShopId);
                res.put("money", money);
                return res;
            }

            String result = CongMingPayProxyService.query(congMingShopId, orderId);
            JSONObject jsonObject = JSONObject.fromObject(result);
            log.info("查询支付订单：{}", jsonObject.toString());
            String resultCode = jsonObject.getString("result_code");
            if ("FAIL".equals(resultCode)) {
                String resultMsg = jsonObject.getString("result_msg");
                throw new ServiceException(resultMsg);
            }
            CmOrderQuery cmOrderQuery = (CmOrderQuery) JSONObject.toBean(jsonObject, CmOrderQuery.class);
            // 查询是否已存在该记录，若存在则更新记录，否则新增记录
            CongMingOrderQuery existQuery = congMingOrderQueryRepository.queryByOrderId(orderId);
            if (null == existQuery) {
                existQuery = new CongMingOrderQuery();
                existQuery.addCongMingOrderQuery(cmOrderQuery, shopId);
                existQuery.setCreateTime(Calendar.getInstance().getTime());
                congMingOrderQueryRepository.save(existQuery);
            } else {
                existQuery.addCongMingOrderQuery(cmOrderQuery, shopId);
                congMingOrderQueryRepository.save(existQuery);
            }

            BigDecimal payable = existQuery.getPayable();
            long status = existQuery.getStatus();
            resultCode = existQuery.getResultCode();
            String cmOrderId = existQuery.getCmOrderId();
            Date time = existQuery.getTime();
            String timeEnd = existQuery.getTimeEnd();
            long refundStatus = existQuery.getRefundStatus();
            String cmShopId = existQuery.getCmShopId();
            BigDecimal money = existQuery.getMoney();

            res.put("payable", payable);
            res.put("status", CmQueryPayStatus.of(status).getDesc());
            res.put("resultCode", resultCode);
            res.put("cmOrderId", cmOrderId);
            res.put("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));
            res.put("timeEnd", timeEnd);
            res.put("refundStatus", CmRefundStatus.of(refundStatus).getDesc());
            res.put("cmShopId", cmShopId);
            res.put("money", money);
            return res;
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        } catch (ParseException e) {
            e.getErrorOffset();
            throw new ServiceException(e.getMessage());
        }
    }
}
